package com.bsj.media.media.G726;

public class G726_32k {

	/**
	 * 返回G726_32K初始化对象
	 * 
	 * @return
	 */
	public static g726_state_s g726_init() {
		g726_state_s state = new g726_state_s();

		state.yl = 34816;
		state.yu = 544;
		state.dms = 0;
		state.dml = 0;
		state.ap = 0;
		state.rate = 32000;

		int i = 0;
		for (i = 0; i < 2; i++) {
			state.a[i] = 0;
			state.pk[i] = 0;
			state.sr[i] = 32;
		}
		for (i = 0; i < 6; i++) {
			state.b[i] = 0;
			state.dq[i] = 32;
		}
		state.td = 0;
		state.bits_per_sample = 4;
		G726_common.bitstream_init(state.bs);

		return state;
	}

	/**
	 * 解码G726_32K 音频
	 * 
	 * @param s
	 * @param amp
	 * @param g726_data
	 * @param g726_bytes
	 * @return 解码后音频长度
	 */
	public static int decoder(g726_state_s s, byte amp[], final byte g726_data[], int inOffset, int g726_bytes) {

		int i;
		int samples;
		byte code;
		short sl;

		i = inOffset; // g726 偏移为
		int len = inOffset + g726_bytes; // 总长度
		for (samples = 0;;) {

			//// G726_PACKING_RGHT
			if (s.getBs().residue < s.getBits_per_sample()) {
				if (i >= len)
					break;
				s.getBs().bitstream |= (g726_data[i++] << s.getBs().residue);
				s.getBs().bitstream &= 0x000000FF;

				s.getBs().residue += 8;
			}
			code = (byte) (s.getBs().bitstream & ((1 << s.getBits_per_sample()) - 1));
			s.getBs().bitstream >>= s.getBits_per_sample();
			s.getBs().bitstream &= 0x000000FF;

			s.getBs().residue -= s.getBits_per_sample();

			sl = g726_decoder(s, code);

			amp[samples++] = (byte) (sl & 0x00ff);
			amp[samples++] = (byte) (((sl & 0xff00) >> 8) & 0x00FF);
		}
		return samples;
	}

	/**
	 * 编码pcm到G726_32K
	 * 
	 * @param s
	 * @param g726_data
	 * @param amp
	 * @param len
	 * @return 返回编码后数据长度
	 */
	public static int g726_encode(g726_state_s s, byte g726_data[], int g726Offset, final byte amp[], int len) {

		int i;
		int g726_bytes = g726Offset;
		short sl;
		byte code;

		for (i = 0; i < len; i += 2) {
			sl = (short) ((amp[i] + (amp[i + 1] << 8)) >> 2);

			code = g726_encoder(s, sl);

			// G726_PACKING_RIGHT
			s.getBs().bitstream |= (code << s.getBs().residue);
			s.getBs().bitstream &= 0x000000FF;
			s.getBs().residue += s.getBits_per_sample();
			if (s.getBs().residue >= 8) {
				g726_data[g726_bytes++] = (byte) (s.getBs().bitstream & 0xFF);
				s.getBs().bitstream >>= 8;
				s.getBs().bitstream &= 0x000000FF;
				s.getBs().residue -= 8;
			}
		}

		return (g726_bytes - g726Offset);
	}

	/*
	 * Decodes a 4-bit CCITT G.726_32 ADPCM code and returns the resulting 16-bit
	 * linear PCM, A-law or u-law sample value.
	 */
	private static short g726_decoder(g726_state_s state, byte code) {
		short sezi;
		short sei;
		short se;
		short sr;
		short dq;
		short dqsez;
		int y;

		/* Mask to get proper bits */
		code &= 0x0F;
		sezi = G726_common.predictor_zero(state);
		sei = (short) (sezi + G726_common.predictor_pole(state));

		y = G726_common.step_size(state);
		dq = G726_common.reconstruct(code & 8, G726_common.g726_32_dqlntab[code], y);

		/* Reconstruct the signal */
		se = (short) (sei >> 1);
		sr = (short) ((dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq));

		/* Pole prediction difference */
		dqsez = (short) (sr + (sezi >> 1) - se);

		G726_common.update(state, y, G726_common.g726_32_witab[code], G726_common.g726_32_fitab[code], dq, sr, dqsez);

		return (short) (sr << 2);
	}

	/*
	 * Encodes a linear input sample and returns its 4-bit code.
	 */
	private static byte g726_encoder(g726_state_s state, short sample) {

		short sei;
		short sezi;
		short se;
		short d;
		short sr;
		short dqsez;
		short dq;
		short i;
		int y;

		sezi = G726_common.predictor_zero(state);
		sei = (short) (sezi + G726_common.predictor_pole(state));
		se = (short) (sei >> 1);
		d = (short) (sample - se);

		/* Quantize the prediction difference */
		y = G726_common.step_size(state);
		i = G726_common.quantize(d, y, G726_common.qtab_726_32, 15);
		dq = G726_common.reconstruct(i & 8, G726_common.g726_32_dqlntab[i], y);

		/* Reconstruct the signal */
		sr = (short) ((dq < 0) ? (se - (dq & 0x3FFF)) : (se + dq));

		/* Pole prediction difference */
		dqsez = (short) (sr + (sezi >> 1) - se);

		G726_common.update(state, y, G726_common.g726_32_witab[i], G726_common.g726_32_fitab[i], dq, sr, dqsez);
		return (byte) i;
	}
}
